package org.geoscrape.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 
 */
public class HtmlParser
{

	public static String getContent(String start, String content)
	{
		return getContent(start, null, content);
	}

	public static String getContent(String start, String stop, String content)
	{
		return getContent(start, stop, content, 0);
	}

	public static String getContent(String start, String stop, String content, int skip)
	{
		int beginIndex = content.indexOf(start, skip);
		if (beginIndex < 0)
		{
			// string not found
			return null;
		}
		if (stop == null)
		{
			return content.substring(beginIndex + start.length());
		}
		else
		{
			beginIndex += start.length();
			int endIndex = content.indexOf(stop, beginIndex);
			if (beginIndex < 0 || endIndex < beginIndex)
			{
				System.out.println("Incorrect index!");
			}
			return content.substring(beginIndex, endIndex);
		}
	}

	/**
	 * Get the contents of a tag. Returns everything after the first ">" and before the last "<" of the tag, including other tags of the same type.
	 * Tag names do not include the opening and closing brackets or any parameters.
	 * 
	 * @param tagName not case sensitive.
	 * @param content
	 * @return
	 */
	public static String getTagContent(String tagName, String content)
	{
		String realContent = content;
		tagName = tagName.toLowerCase();
		String startTag = "<" + tagName;
		String endTag = "</" + tagName;
		content = content.toLowerCase();
		// get the starting point
		int startIndex = content.indexOf(startTag);
		// get the end point of the start tag
		startIndex = content.indexOf(">", startIndex) + 1;
		int level = 1;
		int endIndex = startIndex;
		int tmpIndex = startIndex;
		while (level > 0)
		{
			int a = content.indexOf(startTag, tmpIndex);
			int b = content.indexOf(endTag, tmpIndex);
			if (a > b || a < 0)
			{
				// closing tag first or no opening tag found
				level--;
				endIndex = b;
				tmpIndex = b + 1;
			}
			else if (a < b)
			{
				// opening tag first
				level++;
				tmpIndex = a + 1;
			}
		}

		return realContent.substring(startIndex, endIndex);
	}

	/**
	 * Get the "viewstate" parameters from the page source.
	 * 
	 * @param content
	 *            the page source.
	 * @return
	 */
	public static HashMap<String, String> getViewState(String content)
	{
		HashMap<String, String> res = new HashMap<String, String>();

		// get the first viewstate field
		String viewState = getContent("<input type=\"hidden\" name=\"__VIEWSTATE\" id=\"__VIEWSTATE\" value=\"",
				"\" />", content);
		if (viewState != null)
		{
			res.put("__VIEWSTATE", viewState);
		}
		// get the number of viewstate fields
		String viewStateCount = getContent(
				"<input type=\"hidden\" name=\"__VIEWSTATEFIELDCOUNT\" id=\"__VIEWSTATEFIELDCOUNT\" value=\"", "\" />",
				content);
		if (viewStateCount != null)
		{
			res.put("__VIEWSTATEFIELDCOUNT", viewStateCount);
			int stateCount = Integer.parseInt(viewStateCount);
			for (int x = 1; x <= stateCount; x++)
			{
				viewState = getContent("<input type=\"hidden\" name=\"__VIEWSTATE" + x + "\" id=\"__VIEWSTATE" + x
						+ "\" value=\"", "\" />", content);
				if (viewState != null)
				{
					res.put("__VIEWSTATE" + x, viewState);
				}
			}
		}

		return res;
	}

	/**
	 * Return a list of all instances of text bracketed by the start/stop pair.
	 * 
	 * @param start
	 * @param stop
	 * @param content
	 * @return
	 */
	public static List<String> getAllInstances(String start, String stop, String content)
	{
		List<String> res = new ArrayList<String>();
		int startIndex = content.indexOf(start);
		while (startIndex >= 0)
		{
			int endIndex = content.indexOf(stop, startIndex);
			if (endIndex >= 0)
			{
				res.add(content.substring(startIndex + start.length(), endIndex));
				startIndex = content.indexOf(start, endIndex + stop.length());
			}
			else
			{
				// unclosed tags are ignored
				break;
			}
		}
		return res;
	}

	/**
	 * Convert all html character codewords to corresponding characters. Also
	 * remove unprintable characters
	 * 
	 * @param input
	 * @return
	 */
	public static String convertHtmlChar(String input)
	{
		HashMap<String, String> replace = new HashMap<String, String>();
		replace.put("&quot;", "\"");
		replace.put("&amp;", "&");
		replace.put("\u0007", "\u2407");//bell
		replace.put("\u0008", "");//backspace
		replace.put("\u001B", "\u241B");//esc
		replace.put("\u007F", "");//del
		Pattern p = Pattern.compile("&#.[a-fA-F0-9]+;");
		Matcher m = p.matcher(input);
		while (m.find())
		{
			String match = input.substring(m.start(), m.end());
			if (replace.get(match) == null)
			{
				int number = 0;
				if (match.toLowerCase().startsWith("&#x"))
				{
					number = Integer.parseInt(match.substring(3, match.length() - 1), 16);
				}
				else
				{
					number = Integer.parseInt(match.substring(2, match.length() - 1));
				}
				String replacement = "" + (char) number;
				replace.put(match, replacement);
			}
		}
		for (String key : replace.keySet())
		{
			String replacement = replace.get(key);
			input = input.replaceAll(key, replacement);
		}
		return input;
	}
}
